package boardgamer.yavalath.client.animation;

import android.graphics.Canvas;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import boardgamer.common.client.animation.Animation;
import boardgamer.yavalath.client.game.YavalathDrawableBoard;
import boardgamer.yavalath.client.game.YavalathDrawableTile;
import boardgamer.yavalath.client.resources.ImageHandler;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.collect.Sets;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * The tile ripple effect boardgamer.yavalath.client.animation. When a new token is placed on the game board,
 * neighboring tiles ripple outwards.
 */
public class MoveAnimation extends Animation {

  @VisibleForTesting
  static final int PERIOD = 75;

  private final YavalathDrawableBoard board;
  private final int centerX;
  private final int centerY;
  private final Set<GlowTile> tiles;
  private int radius = 0;

  public MoveAnimation(YavalathDrawableBoard board, int x, int y) {
    super(PERIOD);

    this.board = checkNotNull(board, "board");
    this.tiles = Collections.newSetFromMap(new ConcurrentHashMap<GlowTile, Boolean>());
    this.centerX = x;
    this.centerY = y;
    this.tiles.add(new GlowTile(board, x, y));

    this.onPeriodic();
  }

  @Override
  protected void onPeriodic() {
    radius++;

    tiles.clear();
    tiles.addAll(createTiles(new Point(radius, radius), 0, -1));
    tiles.addAll(createTiles(new Point(radius, 0), -1, -1));
    tiles.addAll(createTiles(new Point(0, -radius), -1, 0));
    tiles.addAll(createTiles(new Point(-radius, -radius), 0, 1));
    tiles.addAll(createTiles(new Point(-radius, 0), 1, 1));
    tiles.addAll(createTiles(new Point(0, radius), 1, 0));
  }

  @Override
  public void draw(Canvas canvas) {
    for (GlowTile tile : tiles) {
      tile.draw(canvas);
    }
  }

  private Set<GlowTile> createTiles(Point relativeStartPoint, int dx, int dy) {
    Set<GlowTile> tiles = Sets.newHashSet();
    for (int step = 0; step < radius; step++) {
      int x = centerX + relativeStartPoint.x + step * dx;
      int y = centerY + relativeStartPoint.y + step * dy;
      if (board.hasTile(x, y)) {
        tiles.add(new GlowTile(board, x, y));
      }
    }
    return tiles;
  }

  @Override
  public boolean isDead() {
    return tiles.isEmpty();
  }

  @VisibleForTesting
  static class GlowTile {

    private static final Paint DEFAULT_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG);

    static {
      DEFAULT_PAINT.setColorFilter(
          new LightingColorFilter(YavalathDrawableTile.DEFAULT_COLOR, 0));
    }

    private final RectF boundingRect;
    private final int x;
    private final int y;

    public GlowTile(YavalathDrawableBoard board, int x, int y) {
      checkArgument(board.hasTile(x, y), "Board does not have tile at (" + x + "," + y + ")");
      this.boundingRect = board.getTile(x, y).getBoundingRect();
      this.x = x;
      this.y = y;
    }

    public void draw(Canvas canvas) {
      canvas.drawBitmap(ImageHandler.tile, null /* srcRect */, boundingRect, DEFAULT_PAINT);
    }

    @Override
    public boolean equals(Object obj) {
      if (!(obj instanceof GlowTile)) {
        return false;
      }

      GlowTile that = (GlowTile) obj;
      return Objects.equal(this.x, that.x)
          && Objects.equal(this.y, that.y);
    }

    @Override
    public int hashCode() {
      return Objects.hashCode(x, y);
    }
  }
}
